//===--- CaptureInfo.cpp - Data Structure for Capture Lists ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/ast/CaptureInfo.h"
#include "polarphp/ast/AstContext.h"
#include "polarphp/ast/Decl.h"
#include "llvm/Support/raw_ostream.h"

namespace polar {

CaptureInfo::CaptureInfo(AstContext &ctx, ArrayRef<CapturedValue> captures,
                         DynamicSelfType *dynamicSelf,
                         OpaqueValueExpr *opaqueValue,
                         bool genericParamCaptures) {
   static_assert(std::is_trivially_destructible<CapturedValue>::value,
                 "Capture info is alloc'd on the ASTContext and not destroyed");
   static_assert(std::is_trivially_destructible<CaptureInfo::CaptureInfoStorage>::value,
                 "Capture info is alloc'd on the ASTContext and not destroyed");

   OptionSet<Flags> flags;
   if (genericParamCaptures)
      flags |= Flags::HasGenericParamCaptures;

   if (captures.empty() && !dynamicSelf && !opaqueValue) {
      *this = CaptureInfo::empty();
      StorageAndFlags.setInt(flags);
      return;
   }

   size_t storageToAlloc =
      CaptureInfoStorage::totalSizeToAlloc<CapturedValue>(captures.size());
   void *storageBuf = ctx.Allocate(storageToAlloc, alignof(CaptureInfoStorage));
   auto *storage = new (storageBuf) CaptureInfoStorage(captures.size(),
                                                       dynamicSelf,
                                                       opaqueValue);
   StorageAndFlags.setPointerAndInt(storage, flags);
   std::uninitialized_copy(captures.begin(), captures.end(),
                           storage->getTrailingObjects<CapturedValue>());
}

CaptureInfo CaptureInfo::empty() {
   static const CaptureInfoStorage empty{0, /*dynamicSelf*/nullptr,
      /*opaqueValue*/nullptr};
   CaptureInfo result;
   result.StorageAndFlags.setPointer(&empty);
   return result;
}

bool CaptureInfo::hasLocalCaptures() const {
   for (auto capture : getCaptures())
      if (capture.getDecl()->getDeclContext()->isLocalContext())
         return true;
   return false;
}


void CaptureInfo::
getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
   if (!hasLocalCaptures()) return;

   Result.reserve(getCaptures().size());

   // Filter out global variables.
   for (auto capture : getCaptures()) {
      if (!capture.getDecl()->getDeclContext()->isLocalContext())
         continue;

      Result.push_back(capture);
   }
}

LLVM_ATTRIBUTE_USED void CaptureInfo::dump() const {
   print(llvm::errs());
   llvm::errs() << '\n';
}

void CaptureInfo::print(raw_ostream &OS) const {
   OS << "captures=(";

   if (hasGenericParamCaptures())
      OS << "<generic> ";
   if (hasDynamicSelfCapture())
      OS << "<dynamic_self> ";
   if (hasOpaqueValueCapture())
      OS << "<opaque_value> ";

   interleave(getCaptures(),
              [&](const CapturedValue &capture) {
                 OS << capture.getDecl()->getBaseName();

                 if (capture.isDirect())
                    OS << "<direct>";
                 if (capture.isNoEscape())
                    OS << "<noescape>";
              },
              [&] { OS << ", "; });
   OS << ')';
}

} // polar
